package controllers;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;

import db.EventDAO;
import db.ServiceDAO;
import db.VehicleDAO;
import java.util.Collections;
import java.util.Comparator;
import java.sql.Date;
import java.sql.Time;
import java.util.GregorianCalendar;
import views.ProgressBar;

public class EventHandler {

    ArrayList<Service> services = new ArrayList<Service>();
    SimpleDateFormat sdf = new SimpleDateFormat("hh:mm a");
    Calendar calendar;

    public EventHandler() {}

    public static void sortEvents(ArrayList<Event> events, Comparator<Event> sorter) {
        Collections.sort(events, sorter);
    }
    private final Comparator<Event> SORT_BY_POSSIBLE_DRIVERS = new Comparator<Event>() {

        @Override
        public int compare(Event e1, Event e2) {
            int e1Drivers = e1.getPossibleDrivers().size();
            int e2Drivers = e2.getPossibleDrivers().size();

            if (e1Drivers //e1 has less drivers
                    < e2Drivers) {
                return -1;
            } else if (e1Drivers //e1 has more drivers
                    > e2Drivers) {
                return 1;
            } else { //same number of drivers
                return 0;
            }
        }
    };
    public static final Comparator<Event> SORT_BY_DEPART_TIME = new Comparator<Event>() {

        @Override
        public int compare(Event e1, Event e2) {
            if (servStart(e1.getService()).before(servStart(e2.getService()))) {
                return -1;
            } else if (servStart(e1.getService()).after(servStart(e2.getService()))) {
                return 1;
            } else {
                return 0;
            }
        }
    };

    public synchronized void generateEvents(Calendar cal) {


        this.calendar = new GregorianCalendar(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));

        Date date = new Date(calendar.getTimeInMillis());

        services = (ArrayList<Service>) ServiceDAO.getServicesByDay(calendar);

        ArrayList<Service> week = new ArrayList<Service>();

        for (Service temp : services) {
            if (calendar.getTimeInMillis() >= temp.getStartDate().getTime()
                    && calendar.getTimeInMillis() <= temp.getEndDate().getTime()) {
                week.add(temp);
                System.out.println(temp.getId() + " " + sdf.format(temp.getStartTime()) + " - " + sdf.format(temp.getEndTime()) + "   " + temp.getDriveTime());
            }

        }

        int count = 0;
        ArrayList<Event> newEvents = new ArrayList<Event>();
        ArrayList<Event> currentEvents = (ArrayList<Event>) EventDAO.getEventsByDate(new Date(calendar.getTimeInMillis()));
        Event tempEvent;
        for (Service temp : week) {
            System.out.println((count++ + 1) + ". " + temp.getId() + " " + sdf.format(temp.getStartTime()) + " - " + sdf.format(temp.getEndTime()) + "   " + temp.getDriveTime());
            tempEvent = new Event(temp.getId());
            tempEvent.setDate(date);
            newEvents.add(tempEvent);
        }
        ProgressBar.setProgress(10);
//        int i = 1;
//        for (Event temp : newEvents) {
//            System.out.println("Event " + i + " date: " + temp.getDate().getTime());
//            i++;
//        }
//
//        i = 1;
//        for (Event current : currentEvents) {
//            System.out.println("Event " + i + " date: " + current.getDate().getTime());
//            i++;
//        }
        float progressCounter = 0;
        boolean canAdd = false;
        for (Event temp : newEvents) {
            if (currentEvents.size() < 1) {
                EventDAO.addEvent(temp);
            } else {
                for (Event current : currentEvents) {
                    canAdd = true;
                    boolean id = (current.getServiceID() == temp.getServiceID());
                    boolean datebol = current.getDate().equals(temp.getDate());
                    if (datebol && id) {
                        canAdd = false;
                        break;
                    }
                }
                if (canAdd == true) {
                    currentEvents.add(temp);
                    EventDAO.addEvent(temp);
                }
            }
            ProgressBar.setProgress(10 + (int) (((++progressCounter) / newEvents.size()) * 23));
        }
        //ProgressBar.setProgress(33);
        progressCounter = 0;
        newEvents = (ArrayList<Event>) EventDAO.getEventsByDate(new Date(calendar.getTimeInMillis()));
        sortEvents(newEvents, SORT_BY_POSSIBLE_DRIVERS);
        ArrayList<TimeSlot> takenSlots = new ArrayList<TimeSlot>();
        //60 sec * 1000 ms * 60 min * 24 hr * 7 days = millisec in a week
        ArrayList<Event> lastWeekEvents = (ArrayList<Event>) EventDAO.getEventsByDate(new Date(calendar.getTimeInMillis() - (60 * 1000 * 60 * 24 * 7)));

        for (Event event : newEvents) {

            for (Event lastWeek : lastWeekEvents) {
                if (lastWeek.getServiceID() == event.getServiceID()
                        && lastWeek.getDefaultDriverID() != 0) {
                    event.setDriverID(lastWeek.getDefaultDriverID());
                    event.setDefaultDriverID(lastWeek.getDefaultDriverID());
                    takenSlots.add(new TimeSlot(lastWeek.getDefaultDriverID(),
                            event.getService().getDay(),
                            new Time(event.getService().getStartTime().getTime() - (event.getService().getDriveTime() * 60 * 1000)),
                            new Time(event.getService().getEndTime().getTime() + (event.getService().getDriveTime() * 60 * 1000)),
                            null));
                    break;
                }
            }
            if (event.getDriverID() == 0) {
                ArrayList<Driver> drivers = (ArrayList<Driver>) event.getPossibleDrivers(takenSlots, (ArrayList<Driver>)event.getPossibleDrivers());

                if (drivers.size() > 0) {
                    int randomNum = (int) (Math.random() * (drivers.size() - 1));
                    event.setDriverID(drivers.get(randomNum).getId());
                    event.setDefaultDriverID(drivers.get(randomNum).getId());
                    takenSlots.add(new TimeSlot(drivers.get(randomNum).getId(),
                            event.getService().getDay(),
                            new Time(event.getService().getStartTime().getTime() - (event.getService().getDriveTime() * 60 * 1000)),
                            new Time(event.getService().getEndTime().getTime() + (event.getService().getDriveTime() * 60 * 1000)),
                            null));
                }
            }
            EventDAO.saveEvent(event);
            ProgressBar.setProgress(33 + (int) (((++progressCounter) / newEvents.size()) * 33));
        }
        //ProgressBar.setProgress(66);
        progressCounter = 0;
        ArrayList<TimeSlot> vTakenSlots = new ArrayList<TimeSlot>();
        ArrayList<Vehicle> allVehicles = (ArrayList<Vehicle>) VehicleDAO.getVehicles();
        for (Event event : newEvents) {

            ArrayList<Vehicle> vehicles = (ArrayList<Vehicle>) event.getPossibleVehicles(vTakenSlots, allVehicles);

            if (vehicles.size() > 0) {
                //use first available vehicle
                event.setVehicleID(vehicles.get(0).getId());
                vTakenSlots.add(new TimeSlot(vehicles.get(0).getId(),
                        event.getService().getDay(),
                        new Time(event.getService().getStartTime().getTime() - (event.getService().getDriveTime() * 60 * 1000)),
                        new Time(event.getService().getEndTime().getTime() + (event.getService().getDriveTime() * 60 * 1000)),
                        null));
            }
            EventDAO.saveEvent(event);
            ProgressBar.setProgress(66 + (int) (((++progressCounter) / newEvents.size()) * 34));
        }
        ProgressBar.setProgress(100);
    }

    private static Time servStart(Service service) {
        return new Time(service.getDepartTime().getTime());
    }
}
